/*********************
* Author:Dzlua
* QQ:505544956
* time:2016/06/01
*/
#include "IEngine.h"
#include "../edui.h"

EDUI_NAMESPACE_BEGIN

IEngine::IEngine() : m_stringGroup(0), m_xmlDir(0)
{}

IEngine::~IEngine()
{
    if (m_xmlDir){
        delete m_xmlDir;
        m_xmlDir = 0;
    }
    if (m_stringGroup){
        delete m_stringGroup;
        m_stringGroup = 0;
    }
}

SurfaceMgr* IEngine::GetSurfaceMgr()
{
    return &m_surfaceMgr;
}

FontMgr* IEngine::GetFontMgr()
{
    return &m_fontMgr;
}

CursorMgr* IEngine::GetCursorMgr()
{
    return &m_cursorMgr;
}

StringMgr* IEngine::GetStringMgr()
{
    return &m_stringMgr;
}

WindowMgr* IEngine::GetWindowMgr()
{
    return &m_windowMgr;
}

#ifdef EDUI_USE_LUAPLUS
LuaMgr* IEngine::GetLuaMgr()
{
    return &m_luaMgr;
}

bool IEngine::RunLua(const char* file)
{
    return m_luaMgr.DoFile(file);
}
#endif

const char* IEngine::GetVersion()
{
    m_ver = "EDUI ";
    m_ver += EDUI_VERSION;
    return m_ver.GetString();
}

void IEngine::SetStringGroup(const char* group)
{
    if (!group) return;
    if (m_stringGroup){
        delete m_stringGroup;
    }
    size_t size = strlen(group) + 1;
    m_stringGroup = new char[size];
    strcpy(m_stringGroup, group);
    m_stringGroup[size-1] = 0;

    Msg msg;
    msg.msg = MSG_WND_EXPOSED;
    msg.wnd.data1 = 0;
    WindowMgr* wndMgr = this->GetWindowMgr();
    for (size_t i = 0; i < wndMgr->Count(); ++i){
        msg.wnd.wnd = wndMgr->GetByIndex(i);
        this->PushMsg(&msg);
    }
}

const char* IEngine::GetStringGroup() const
{
    return m_stringGroup;
}

const char* IEngine::GetText(const char* name, const char* nilText)
{
    const char* text = m_stringMgr.Get(name, m_stringGroup);
    return ((text == 0) ? nilText : text);
}

const char* IEngine::GetText(int id, const char* nilText)
{
    if (id < 0) return nilText;
    const char* text = m_stringMgr.Get(id, m_stringGroup);
    return ((text == 0) ? nilText : text);
}

void IEngine::SetXmlDir(const char* dir)
{
    if (!dir) return;
    if (m_xmlDir){
        delete m_xmlDir;
    }
    size_t size = strlen(dir) + 1;
    m_xmlDir = new char[size];
    strcpy(m_xmlDir, dir);
    m_xmlDir[size-1] = 0;
}

const char* IEngine::GetXmlDir()
{
    return m_xmlDir;
}

IWindow* IEngine::CreateWindow(const char* name, const char* title, int x, int y, int w, int h, Uint32 flags, int index)
{
    IWindow* wnd = this->GetWindowInterface();
    if (!wnd) return 0;
    if (!wnd->Create(name, title, x, y, w, h, flags, index)){
        wnd->Release();
        return 0;
    }
    return wnd;
}

void IEngine::SetError(const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    m_errmsg.Format(fmt, args);
    va_end(args);
}

const char* IEngine::GetError()
{
    return m_errmsg.GetString();
}

void IEngine::ClearError()
{
    m_errmsg.Empty();
}

IWindow* IEngine::FindWindow(const char* name)
{
    return m_windowMgr.GetByName(name);
}

IWindow* IEngine::FindWindow(Uint32 wndID)
{
    return m_windowMgr.GetByID(wndID);
}

IFont* IEngine::FindFont(const char* name)
{
    return m_fontMgr.Get(name);
}

IFont* IEngine::FindFont(int id)
{
    return m_fontMgr.Get(id);
}

ICursor* IEngine::FindCursor(const char* name)
{
    return m_cursorMgr.Get(name);
}

ICursor* IEngine::FindCursor(int id)
{
    return m_cursorMgr.Get(id);
}

ISurface* IEngine::FindSurface(const char* name)
{
    return m_surfaceMgr.Get(name);
}

ISurface* IEngine::FindSurface(int id)
{
    return m_surfaceMgr.Get(id);
}

void IEngine::DispatchMsg(Msg* msg)
{
    WndMsg* wmsg = &msg->wnd;
#ifndef _WIN32
    // linux : ctrl+c cmd,quit all.
    // windows : right mouse close, don't quit all.
    if (msg->msg == MSG_QUIT){
        WindowMgr* wndMgr = this->GetWindowMgr();
        for (size_t i = 0; i < wndMgr->Count(); ++i){
            wmsg->wnd = wndMgr->GetByIndex(i);
            if (wmsg->wnd){
                wmsg->wnd->Destroy();
                wmsg->wnd->Release();
            }
        }
        wndMgr->Clear();
        return;
    }
#endif

    if (wmsg->wnd){
        if (wmsg->wnd->IsEnabled()){
            wmsg->wnd->HandleMsg(msg);
        }
    } else {
        // notify all window
        WindowMgr* wndMgr = this->GetWindowMgr();
        for (size_t i = 0; i < wndMgr->Count(); ++i){
            wmsg->wnd = wndMgr->GetByIndex(i);
            if (wmsg->wnd && wmsg->wnd->IsEnabled()){
                wmsg->wnd->HandleMsg(msg);
            }
        }
    }
}

bool IEngine::LoadString(const char* name, const char* value, const char* group, int id)
{
    if (!m_stringMgr.Add(name, value, group, id)) return false;
    return true;
}

bool IEngine::_LoadRes(TiXmlDocument* xml)
{
    TiXmlElement* root = xml->FirstChildElement();
    if (!root) return false;
 
    for(TiXmlElement* child = root->FirstChildElement(); child; child = child->NextSiblingElement()){
		// do something
        if (strcmp(child->Value(), "Include") == 0){
            if (!this->LoadRes(child->Attribute("source"))) return false;
            continue;
        } else if (strcmp(child->Value(), "Font") == 0){
            //id,name,source,size
            int id = -1;
            int size = 12;
            if (!child->Attribute("id", &id)) id = -1;
            if (!child->Attribute("size", &size)) size = 12;
            const char* name = child->Attribute("name");
            const char* source = child->Attribute("source");

            bool bold, italic, underline, strikethrough;
            bold = italic = underline = strikethrough = false;
            child->QueryBoolAttribute("bold", &bold);
            child->QueryBoolAttribute("italic", &italic);
            child->QueryBoolAttribute("underline", &underline);
            child->QueryBoolAttribute("strikethrough", &strikethrough);
            
            int                 style  = FONT_STYLE_NORMAL;
            if (bold)           style |= FONT_STYLE_BOLD;
            if (italic)         style |= FONT_STYLE_ITALIC;
            if (underline)      style |= FONT_STYLE_UNDERLINE;
            if (strikethrough)  style |= FONT_STYLE_STRIKETHROUGH;

            if (!this->LoadFont(source, size, style, name, id)){
                Log("[EDUI] Engine::LoadRes() load Font error. %s", child->GetText());
            }
        } else if (strcmp(child->Value(), "Image") == 0){
            //id,name,source
            int id = -1;
            if (!child->Attribute("id", &id)) id = -1;
            const char* name = child->Attribute("name");
            const char* source = child->Attribute("source");

            if (!this->LoadSurface(source, name, id)){
                Log("[EDUI] Engine::LoadRes() load Image error. id->%d,name->%s,source->%s",
                        id, (name == 0) ? "" : name, (source == 0) ? "" : source);
            }
        } else if (strcmp(child->Value(), "Cursor") == 0){
            //id,name,source
            int id = -1, hotX, hotY;
            if (!child->Attribute("id", &id)) id = -1;
            if (!child->Attribute("hotx", &hotX)) hotX = 0;
            if (!child->Attribute("hoty", &hotY)) hotY = 0;
            const char* name = child->Attribute("name");
            const char* image = child->Attribute("image");
            const char* sys = child->Attribute("sys");

            if (image){
                if (!this->LoadCursor(image, hotX, hotY, name, id)){
                    Log("[EDUI] Engine::LoadRes() load Cursor error. id->%d,name->%s,image->%s",
                            id, (name == 0) ? "" : name, (image == 0) ? "" : image);
                }
            } else {
                SystemCursor sysid = GetSystemCursorID(sys);
                if (SYSTEM_CURSOE_ERROR == sysid || !this->LoadCursor(sysid, name, id)){
                    Log("[EDUI] Engine::LoadRes() load Cursor error. id->%d,name->%s,sys->%s",
                            id, (name == 0) ? "" : name, (sys == 0) ? "" : sys);
                }
            }
        } else if (strcmp(child->Value(), "String") == 0){
            //id,name,group,value
            int id = -1;
            if (!child->Attribute("id", &id)) id = -1;
            const char* name = child->Attribute("name");
            const char* group = child->Attribute("group");
            const char* value = child->Attribute("value");

            if (!this->LoadString(name, value, group, id)){
                Log("[EDUI] Engine::LoadRes() load String error. id->%d,name->%s,group->%s,value->%s",
                        id, (name == 0) ? "" : name, (group == 0) ? "" : group, (value == 0) ? "" : value);
            }
        }
	}
    return true;
}

// load xml to load font,image,string
bool IEngine::LoadRes(const char* xmlfile, bool disableXmlDir)
{
    if (!xmlfile) return false;

    String file = xmlfile;
    if (!disableXmlDir) file.Format("%s%s", this->GetXmlDir(), xmlfile);

    TiXmlDocument xml;
    xml.LoadFile(file.GetString());
    if (xml.Error()) goto error;

    if (!this->_LoadRes(&xml)) goto error;

    return true;
error:
    Log("[EDUI] Engine::LoadRes(\"%s\") error. : %s", file.GetString(), xml.ErrorDesc());
    return false;
}

bool IEngine::LoadResFromStr(const char* xmlstr)
{
    if (!xmlstr) return false;

    TiXmlDocument xml;
    xml.Parse(xmlstr, 0, TIXML_ENCODING_UTF8);
    if (xml.Error()) goto error;

    if (!this->_LoadRes(&xml)) goto error;

    return true;
error:
    Log("[EDUI] Engine::LoadResFromStr(\"%s\") error. : %s", xmlstr, xml.ErrorDesc());
    return false;
}

bool IEngine::_BuildControl(Control** rootctrl, IWindow* wnd, Container* parent, TiXmlElement* root)
{
    if (!root) return false;
    for(TiXmlElement* child = root->FirstChildElement(); child; child=child->NextSiblingElement()){
        Control* ctrl = 0;
        Container* cont = 0;
        if (strcmp(child->Value(), "Include") == 0){
            ctrl = this->LoadControl(wnd, child->Attribute("source"));
            if (!ctrl) goto error;
            if (!parent){
                if (*rootctrl) goto error;
                *rootctrl = ctrl;
            } else{
                if (!parent->Add(ctrl)) goto error;
            }
            continue;
        } else if (strcmp(child->Value(), "Animation") == 0){
            wnd->LoadAnimation(child);
            continue;
        }

        ctrl = this->GetControlInterface(child->Value());
        if (!ctrl){
            Log("[EDUI] Engine::BuildControl : creare control %s error!", child->Value());
            return false;
        }
        
        ctrl->SetWindow(wnd);
        ctrl->LoadLayout(child);

        if (!parent){
            if (*rootctrl) goto error;
            *rootctrl = ctrl;
        } else{
            if (!parent->Add(ctrl)) goto error;
        }

        cont = Container::ToContainer(ctrl);
        if (cont){
            if (!this->_BuildControl(rootctrl, wnd, cont, child)) goto error;
        }

        continue;
    error:
        if (*rootctrl){
            (*rootctrl)->Release();
            (*rootctrl) = 0;
        }
        if (ctrl){
            ctrl->Release();
            ctrl = 0;
        }
        return false;
    }
    return true;
}

bool IEngine::LoadLayout(IWindow* wnd, const char* xmlfile, bool disableXmlDir)
{
    if (!xmlfile || !wnd || !wnd->IsOK()) return false;

    Control* ctrl = 0;
    TiXmlElement* root = 0;

    String file = xmlfile;
    if (!disableXmlDir) file.Format("%s%s", this->GetXmlDir(), xmlfile);

    TiXmlDocument xml;
    xml.LoadFile(file.GetString());
    if (xml.Error()) goto error;

    root = xml.FirstChildElement("Window");
    if (!root) goto error;

    for (TiXmlAttribute* attr = root->FirstAttribute(); attr; attr = attr->Next()){
        wnd->SetAttribute(attr->Name(), attr->Value());
    }

    wnd->AfterSetAttribute();

    if (!this->_BuildControl(&ctrl, wnd, 0, root)) goto error;

    if (!wnd->SetRoot(Container::ToContainer(ctrl))) goto error;

    return true;
error:
    if (ctrl){
        ctrl->Release();
    }
    Log("[EDUI] Engine::LoadLayout(%s,\"%s\") error. : %s", wnd->GetName(), file.GetString(), xml.ErrorDesc());
    return false;
}

bool IEngine::LoadLayoutFromStr(IWindow* wnd, const char* xmlstr)
{
    if (!xmlstr || !wnd || !wnd->IsOK()) return false;

    Control* ctrl = 0;
    TiXmlElement* root = 0;

    TiXmlDocument xml;
    xml.Parse(xmlstr, 0, TIXML_ENCODING_UTF8);
    if (xml.Error()) goto error;

    root = xml.FirstChildElement("Window");
    if (!root) goto error;

    for (TiXmlAttribute* attr = root->FirstAttribute(); attr; attr = attr->Next()){
        wnd->SetAttribute(attr->Name(), attr->Value());
    }

    wnd->AfterSetAttribute();

    if (!this->_BuildControl(&ctrl, wnd, 0, root)) goto error;

    if (!wnd->SetRoot(Container::ToContainer(ctrl))) goto error;    

    return true;
error:
    if (ctrl){
        ctrl->Release();
    }
    Log("[EDUI] Engine::LoadLayoutFromStr(%s,\"%s\") error. : %s", wnd->GetName(), xmlstr, xml.ErrorDesc());
    return false;
}

Control* IEngine::LoadControl(IWindow* wnd, const char* xmlfile, bool disableXmlDir)
{
    if (!xmlfile || !wnd || !wnd->IsOK()) return 0;

    Control* ctrl = 0;

    String file = xmlfile;
    if (!disableXmlDir) file.Format("%s%s", this->GetXmlDir(), xmlfile);

    TiXmlDocument xml;
    xml.LoadFile(file.GetString());
    if (xml.Error()) goto error;
    
    if (!this->_BuildControl(&ctrl, wnd, 0, xml.RootElement())) goto error;

    return ctrl;
error:
    if (ctrl){
        ctrl->Release();
    }
    Log("[EDUI] Engine::LoadControl(%s,\"%s\") error. : %s", wnd->GetName(), file.GetString(), xml.ErrorDesc());
    return 0;
}

Control* IEngine::LoadControlFromStr(IWindow* wnd, const char* xmlstr)
{
    if (!xmlstr || !wnd || !wnd->IsOK()) return 0;

    Control* ctrl = 0;

    TiXmlDocument xml;
    xml.Parse(xmlstr, 0, TIXML_ENCODING_UTF8);
    if (xml.Error()) goto error;

    if (!this->_BuildControl(&ctrl, wnd, 0, xml.RootElement())) goto error;

    return ctrl;
error:
    if (ctrl){
        ctrl->Release();
    }
    Log("[EDUI] Engine::LoadControlFromStr(%s,\"%s\") error. : %s", wnd->GetName(), xmlstr, xml.ErrorDesc());
    return 0;
}

bool IEngine::LoadFont(const char* file, int size, const char* name, int id)
{
    if (!file) return false;
    IFont* font = this->GetFontInterface();
    if (!font->Create(file, size, name, id)){
        font->Release();
        return false;
    }
    return true;
}

bool IEngine::LoadFont(const char* file, int size, int style, const char* name, int id)
{
    if (!file) return false;
    IFont* font = this->GetFontInterface();
    if (!font->Create(file, size, name, id)){
        font->Release();
        return false;
    }
    if (style) font->SetStyle(style);
    return true;
}

bool IEngine::LoadCursor(const char* surfaceName, int hotX, int hotY, const char* name, int id)
{
    if (!surfaceName) return false;
    ISurface* surface = m_surfaceMgr.Get(surfaceName);
    if (!surface) return false;

    ICursor* cursor = this->GetCursorInterface();
    if (!cursor->Create(surface, hotX, hotY, name, id)){
        cursor->Release();
        return false;
    }
    return true;
}

bool IEngine::LoadCursor(SystemCursor sysid, const char* name, int id)
{
    ICursor* cursor = this->GetCursorInterface();
    if (!cursor->Create(sysid, name, id)){
        cursor->Release();
        return false;
    }
    return true;
}

bool IEngine::LoadSurface(const char* file, const char* name, int id)
{
    if (!file) return false;
    ISurface* surface = this->GetSurfaceInterface();
    if (!surface->Create(file, name, id)){
        surface->Release();
        return false;
    }
    return true;
}

bool IEngine::LoadSurface(void* mem, int size, const char* name, int id)
{
    if (!name || !mem || size <= 0) return false;
    ISurface* surface = this->GetSurfaceInterface();
    if (!surface->Create(mem, size, name, id)){
        surface->Release();
        return false;
    }
    return true;
}

//load texture from surface to wnd.
bool IEngine::LoadTexture(IWindow* wnd, const char* surfaceName, const char* name, int id)
{
    if (!wnd || !surfaceName) return false;
    ISurface* surface = m_surfaceMgr.Get(surfaceName);
    if (!surface) return false;
    ITexture* texture = this->GetTextureInterface();
    if (!texture->Create(wnd->GetRender(), surface, name, id)){
        texture->Release();
        return false;
    }
    return true;
}

bool IEngine::LoadTexture(IWindow* wnd, size_t surfaceIndex, const char* name, int id)
{
    if (!wnd) return false;
    ISurface* surface = m_surfaceMgr.GetByIndex(surfaceIndex);
    if (!surface) return false;
    ITexture* texture = this->GetTextureInterface();
    if (!texture->Create(wnd->GetRender(), surface, name, id)){
        texture->Release();
        return false;
    }
    return true;
}

bool IEngine::LoadAnimation(IWindow* wnd, const char* name, int id)
{
    if (!wnd || !name) return false;
    Animation* animation = Animation::GetInterface();
    if (!animation->Create(wnd, name, id)){
        animation->Release();
        return false;
    }
    return true;
}

bool IEngine::LoadAnimationFrame(IWindow* wnd, const char* name, const char* img, int id)
{
    if (!wnd) return false;
    AnimationMgr* mgr = wnd->GetAnimationMgr();
    Animation* animation = mgr->Get(name);
    if (!animation) return false;
    return animation->AddFrame(img, id);
}

bool IEngine::LoadAnimationFrame(IWindow* wnd, const char* name, int imgid, int id)
{
    if (!wnd) return false;
    AnimationMgr* mgr = wnd->GetAnimationMgr();
    Animation* animation = mgr->Get(name);
    if (!animation) return false;
    return animation->AddFrame(imgid, id);
}

bool IEngine::LoadAnimationFrame(IWindow* wnd, const char* name, AnimationFrame* frame, int id)
{
    if (!wnd) return false;
    AnimationMgr* mgr = wnd->GetAnimationMgr();
    Animation* animation = mgr->Get(name);
    if (!animation) return false;
    return animation->AddFrame(frame, id);
}

IWindow* IEngine::LoadWindow(const char* xmlfile, bool disableXmlDir)
{
    if (!xmlfile) return 0;
    String name;
    name.Format("EDUI%d", m_windowMgr.Count());
    IWindow* wnd = this->CreateWindow(name.GetString(), "EDUI", WINDOWPOS_CENTERED, WINDOWPOS_CENTERED, 640, 480, WINDOW_BORDERLESS);
    if (!wnd) return 0;
    if (!this->LoadLayout(wnd, xmlfile, disableXmlDir)){
        wnd->Close();
        return 0;
    }
    return wnd;
}

IWindow* IEngine::LoadWindowFromStr(const char* xmlstr)
{
    if (!xmlstr) return 0;
    String name;
    name.Format("EDUI%d", m_windowMgr.Count());
    IWindow* wnd = this->CreateWindow(name.GetString(), "EDUI", WINDOWPOS_CENTERED, WINDOWPOS_CENTERED, 640, 480, WINDOW_BORDERLESS);
    if (!wnd) return 0;
    if (!this->LoadLayoutFromStr(wnd, xmlstr)){
        wnd->Close();
        return 0;
    }
    return wnd;
}

Control* IEngine::GetControlInterface(const char* name)
{
    if (0 == strcmp(name, "Container")){
        return Container::GetInterface();
    } else if (0 == strcmp(name, "VerticalLayout")){
        return VerticalLayout::GetInterface();
    } else if (0 == strcmp(name, "HorizontalLayout")){
        return HorizontalLayout::GetInterface();
    } else if (0 == strcmp(name, "TableLayout")){
        return TableLayout::GetInterface();
    } else if (0 == strcmp(name, "TileLayout")){
        return TileLayout::GetInterface();
    } else if (0 == strcmp(name, "Control")){
        return Control::GetInterface();
    } else if (0 == strcmp(name, "Label")){
        return Label::GetInterface();
    } else if (0 == strcmp(name, "Link")){
        return Link::GetInterface();
    } else if (0 == strcmp(name, "Text")){
        return Text::GetInterface();
    } else if (0 == strcmp(name, "Edit")){
        return Edit::GetInterface();
    } else if (0 == strcmp(name, "Button")){
        return Button::GetInterface();
    } else if (0 == strcmp(name, "CheckBox")){
        return CheckBox::GetInterface();
    } else if (0 == strcmp(name, "Progress")){
        return Progress::GetInterface();
    } else if (0 == strcmp(name, "Slider")){
        return Slider::GetInterface();
    } else if (0 == strcmp(name, "ScrollBar")){
        return ScrollBar::GetInterface();
    } else if (0 == strcmp(name, "WndCtrl")){
        return WndCtrl::GetInterface();
    }
    return 0;
}
    
EDUI_NAMESPACE_END
